package com.example.printlib;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.hiti.usb.bitmapmanager.BitmapMonitor;
import com.hiti.usb.bitmapmanager.BitmapMonitorResult;
import com.hiti.usb.jni.JniData.IntArray;
import com.hiti.usb.printer.PrinterJob;
import com.hiti.usb.printer.PrinterStatus;
import com.hiti.usb.service.Action;
import com.hiti.usb.service.ErrorCode;
import com.hiti.usb.service.ServiceConnector;
import com.hiti.usb.utility.ByteUtility;
import com.hiti.usb.utility.FileUtility;
import com.hiti.usb.utility.MobileInfo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;


public class MainActivity extends Activity implements OnClickListener {
	private static final String TAG = "MainActivity";

	private final String tag = MainActivity.class.getSimpleName();

	Button m_startService, m_stopServie, m_printerInfo, m_printPhoto, m_resetPrinter, m_resumeJob,
			m_clearText, m_serviceStatus, m_ejectJam, m_setAutoPowerOff, m_cleanPaperPath, m_updateFW;

	TextView m_info, m_serviceInfo;
	ImageView m_infoView;

	//MyReceiver receiver;
	ServiceConnector serviceConnector;
	PrinterOperation operation;

	// print photo select
	private int PaperType;
	private short		MATTE, PRINTCOUNT,PRINTMODE;

	//update firmware
	String m_fwversion, m_fwpath, m_fwfolderpath, m_fwBootpath, m_fwKernelpath;

	ScheduledExecutorService exec3, exec2;

	// print photo select
	String mSelectedPath = "";
	String m_strTablesCopyRoot = "";
	String m_strTablesRoot = "";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		Log.v(tag, "onCreate");

		//Create and Copy color bin file from asset folder
		m_strTablesCopyRoot = this.getExternalFilesDir(null).getAbsolutePath();
		m_strTablesRoot = this.getExternalFilesDir(null).getAbsolutePath()+"/Tables";
		if(!FileUtility.FileExist(m_strTablesRoot)) {
			FileUtility.CreateFolder(m_strTablesRoot);
		}

		copyFileOrDir("Tables");


		// initialize component objects
		m_startService 	= (Button) findViewById(R.id.b_startService);
		m_stopServie 	= (Button) findViewById(R.id.b_stopService);
		m_printerInfo 	= (Button) findViewById(R.id.b_printerInfo);
		m_printPhoto 	= (Button) findViewById(R.id.b_printPhoto);
		m_resetPrinter 	= (Button) findViewById(R.id.b_resetPrinter);
		m_resumeJob 	= (Button) findViewById(R.id.b_resumeJob);
		m_ejectJam  	= (Button) findViewById(R.id.b_ejectPaperJam);
		m_cleanPaperPath= (Button) findViewById(R.id.b_cleanPaperPath);
		m_clearText 	= (Button) findViewById(R.id.b_clearText);
		m_serviceStatus = (Button) findViewById(R.id.b_serviceStatus);
		m_setAutoPowerOff = (Button) findViewById(R.id.b_setAutoPowerOff);
		m_updateFW = (Button) findViewById(R.id.b_updateFW);
		m_info 			= (TextView) findViewById(R.id.t_info);
		m_serviceInfo 	= (TextView) findViewById(R.id.t_service);
		m_infoView 		= (ImageView) findViewById(R.id.m_infoView);

		//init value
		PaperType = 2; //Set printout size. 2:4x6, 3:5x7, 4:6x8
		MATTE = 1; //1:matte, 0:notmatte
		PRINTCOUNT = 1; //Want to print count
		PRINTMODE = 0; //Only for P232W, default 0. 1:fine mode(HOD), 0:standard mode

		InitialValueFW();
		InitialP525FW();

		// set click listener
		m_startService.setOnClickListener(this);
		m_stopServie.setOnClickListener(this);
		m_printerInfo.setOnClickListener(this);
		m_printPhoto.setOnClickListener(this);
		m_resetPrinter.setOnClickListener(this);
		m_resumeJob.setOnClickListener(this);
		m_clearText.setOnClickListener(this);
		m_serviceStatus.setOnClickListener(this);
		m_ejectJam.setOnClickListener(this);
		m_setAutoPowerOff.setOnClickListener(this);
		m_cleanPaperPath.setOnClickListener(this);
		m_updateFW.setOnClickListener(this);
		//
		// Initialize Broadcast receiver for dealing with information returned from service.
		//
		//receiver = new MyReceiver();

		//
		// Get service connector
		// it must be done before doing any service operation.
		//
		serviceConnector = ServiceConnector.register(this, null);
//		serviceConnector.setUploadListener(new UploadListener());

		operation = new PrinterOperation(this, serviceConnector);
		//operation.m_strTablesRoot = m_strTablesRoot;
		operation.m_strTablesRoot = "";

//		Log.e("m_fwpath", m_fwpath);
		Log.e("m_strTablesRoot", m_strTablesRoot);
//		Timer timer = new Timer();
//		timer.scheduleAtFixedRate(new ClockTask(), 3000, 3000);

//		future = scheduledFuture.scheduleAtFixedRate(object : TimerTask(){
//			override fun run() {
//
//			}
//		}, 3000, 3000, TimeUnit.MICROSECONDS)

		//Create temp folder
//		m_strTmpRoot = this.getExternalFilesDir(null).getAbsolutePath() + "/temp";
//		File folder = new File(m_strTmpRoot);
//		boolean success = true;
//		if (!folder.exists()) {
//			success = folder.mkdirs();
//		}
//		if (success) {
//			// Do something on success
//		} else {
//			// Do something else on failure
//		}
	}

	private void copyFileOrDir(String path) {
		AssetManager assetManager = this.getAssets();
		String assets[] = null;
		try {
			assets = assetManager.list(path);
			if (assets.length == 0) {
				copyFile(path);
			} else {
				String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
				File dir = new File(fullPath);
				if (!dir.exists())
					dir.mkdir();
				for (int i = 0; i < assets.length; ++i) {
					copyFileOrDir(path + "/" + assets[i]);
				}
			}
		} catch (IOException ex) {
			Log.e("tag", "I/O Exception", ex);
		}
	}

	private void copyFile(String filename) {
		AssetManager assetManager = this.getAssets();

		InputStream in = null;
		OutputStream out = null;
		try {
			Log.e(tag, "filename: "+filename);
			Log.e(tag, "m_strTablesCopyRoot: "+m_strTablesCopyRoot);
			in = assetManager.open(filename);
			String newFileName = m_strTablesCopyRoot + "/" + filename;
			out = new FileOutputStream(newFileName);

			byte[] buffer = new byte[1024];
			int read;
			while ((read = in.read(buffer)) != -1) {
				out.write(buffer, 0, read);
			}
			in.close();
			in = null;
			out.flush();
			out.close();
			out = null;
		} catch (Exception e) {
			Log.e("tag", e.getMessage());
		}

	}

	private class ClockTask extends TimerTask {
		@Override
		public void run() {
			PrinterJob job = null;
			operation.m_strTablesRoot = m_strTablesRoot;
			job = operation.getPrinterStatus();
			final String ret = retrieveData(job);
			Log.e("timer1", ret);
			showResponse(job);
		}
	}

	private class ClockTask1 extends TimerTask {
		@Override
		public void run() {
			PrinterJob job = null;
//			operation.PRINTCOUNT = PRINTCOUNT;
//			operation.MATTE = MATTE;
//			operation.PRINTMODE = PRINTMODE;
//			operation.PaperType = PaperType;
//			operation.m_strTablesRoot = m_strTablesRoot;
//			job = operation.print("photo3");
//			showResponse(job);

			job = operation.getRibbonInfo();
			final String ret = retrieveData(job);
			Log.e("timer2", ret);
			showResponse(job);
		}
	}

	private void InitialValueFW()
	{
		//FW version and path
		m_fwversion = "1.16.0.Z";
		m_fwfolderpath = this.getExternalFilesDir(null).getAbsolutePath()+"/HiTi_FW";
		m_fwpath = m_fwfolderpath + "/ROM_ALL_p520l.bin";
		Log.i(TAG, "InitialValueFW  m_fwpath: "+m_fwpath);
		if(!FileUtility.FileExist(m_fwfolderpath))
		{
			FileUtility.CreateFolder(m_fwfolderpath);
		}
		//Copy asset fw to absolutepath
		AssetManager assetManager = this.getAssets();
		InputStream in = null;
		OutputStream out = null;
		try {
			in = assetManager.open("HiTi_FW/ROM_ALL_p520l.bin");
			out = new FileOutputStream(m_fwpath);
			byte[] buffer = new byte[1024];
			int read;
			while ((read = in.read(buffer)) != -1) {
				out.write(buffer, 0, read);
			}
			in.close();
			in = null;
			out.flush();
			out.close();
			out = null;
		} catch (Exception e) {
			Log.e("tag", e.getMessage());
		}
	}

	private void InitialP525FW()
	{
		//FW version and path
		m_fwversion = "1.02.0.m";
		m_fwfolderpath = this.getExternalFilesDir(null).getAbsolutePath()+"/HiTiP525N_FW";
		m_fwpath = m_fwfolderpath + "/rootfs.brn";
		m_fwBootpath = m_fwfolderpath + "/boot.brn";
		m_fwKernelpath = m_fwfolderpath + "/kernel.brn";
		if(!FileUtility.FileExist(m_fwpath))
		{
			m_fwpath = null;
			m_fwBootpath = null;
			m_fwKernelpath = null;
		}
		if(!FileUtility.FileExist(m_fwBootpath))
		{
			m_fwBootpath = null;
		}
		if(!FileUtility.FileExist(m_fwKernelpath))
		{
			m_fwKernelpath = null;
		}
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		Log.v(tag, "onResume");
		super.onResume();

	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		Log.v(tag, "onPause");

		super.onPause();
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		Log.v(tag, "onDestroy");

		//
		// Disconnect USB Service Connector
		// It is used to release service connector or resource will not be released.
		//
		serviceConnector.unregister();

		super.onDestroy();
	}


	@Override
	public void onClick(View v) {

		String actionName = (String)v.getTag();
		Log.i(tag, "onClick actionName: "+actionName);
		ErrorCode errorCode = null;

		int id = v.getId();//
// Start background service
//
		if (id == R.id.b_startService) {
			errorCode = serviceConnector.StartService();
			//printerService(actionName, null, errorCode);

			//exec3 = Executors.newSingleThreadScheduledExecutor();
			//exec3.scheduleAtFixedRate(new ClockTask(), 3000, 3000, TimeUnit.MILLISECONDS);
			//exec2 = Executors.newSingleThreadScheduledExecutor();
			//exec2.scheduleAtFixedRate(new ClockTask1(), 3000, 3000, TimeUnit.MILLISECONDS);

			appendInfo(actionName, errorCode);

			//
			// Stop and destroy background service
			//
		} else if (id == R.id.b_stopService) {
			errorCode = serviceConnector.StopService();
			//printerService(actionName, null, errorCode);
			exec3.shutdown();
			exec2.shutdown();
			appendInfo(actionName, errorCode);

			/* print photo */
		} else if (id == R.id.b_printPhoto) {//					operatePrinter(Action.USB_PRINT_PHOTOS);
			callActionSelectorDiag(null, printPhotoPathMap, "photoSelect");

			/* reset printer */
		} else if (id == R.id.b_resetPrinter) {
			operatePrinter(Action.USB_COMMAND_RESET_PRINTER);

			/* clear error and continue job */
		} else if (id == R.id.b_resumeJob) {
			operatePrinter(Action.USB_COMMAND_RESUME_JOB);
		} else if (id == R.id.b_updateFW) {
			operatePrinter(Action.USB_COMMAND_UPDATE_FW);
		} else if (id == R.id.b_ejectPaperJam) {
			operatePrinter(Action.USB_EJECT_PAPER_JAM);
		} else if (id == R.id.b_setAutoPowerOff) {
			operatePrinter(Action.USB_SET_AUTO_POWER_OFF);
		} else if (id == R.id.b_clearText) {
			m_info.setText("");
			m_infoView.setImageBitmap(null);
		} else if (id == R.id.b_serviceStatus) {
			m_serviceInfo.setText(serviceConnector.getHitiServiceStatus());
		} else if (id == R.id.b_printerInfo) {
			callActionSelectorDiag(printerInfoDiag, null, "Printer information");
		} else if (id == R.id.b_cleanPaperPath) {
			operatePrinter(Action.USB_COMMAND_CLEAN_PAPER_PATH);//USB_CLEAN_PAPER_PATH);
		}

	}

	void appendInfo(String name, ErrorCode errorCode) {
		if(errorCode != null && errorCode != ErrorCode.ERR_CODE_SUCCESS) {
			StringBuilder bu = new StringBuilder("\n>>>").append(name)
					.append(": err<0x").append(Integer.toHexString(errorCode.value)).append(" ")
					.append(errorCode.description).append(">");
			m_info.append(bu.toString());
		}
	}


	//------------------------------------------------------------------
	// Dialog related.
	//------------------------------------------------------------------

	static Map<String, Action> printerInfoDiag;
	static Map<String, String> printPhotoPathMap;

	static {
		printerInfoDiag = new LinkedHashMap<>();

		printerInfoDiag.put("Printer status", Action.USB_CHECK_PRINTER_STATUS);
		printerInfoDiag.put("Model name", Action.USB_DEVICE_MODEL_NAME);
		printerInfoDiag.put("Serial number", Action.USB_DEVICE_SERIAL_NUM);
		printerInfoDiag.put("Firmware version", Action.USB_DEVICE_FW_VERSION);
		printerInfoDiag.put("Ribbon information", Action.USB_DEVICE_RIBBON_INFO);
		printerInfoDiag.put("Print count", Action.USB_DEVICE_PRINT_COUNT);
		/*printerInfoDiag.put("Get Storage ID", Action.USB_GET_STORAGE_ID);
		printerInfoDiag.put("Get Object Number", Action.USB_GET_OBJECT_NUMBER);
		printerInfoDiag.put("Get Object Handle ID", Action.USB_GET_OBJECT_HANDLE_ID);
		printerInfoDiag.put("Get Object Info", Action.USB_GET_OBJECT_INFO);
		printerInfoDiag.put("Get Object data", Action.USB_GET_OBJECT_DATA);*/

		printPhotoPathMap = new LinkedHashMap<>();

		printPhotoPathMap.put("4x6 , photo1", "photo1");
		printPhotoPathMap.put("photo2", "photo2");
		printPhotoPathMap.put("photo3", "photo3");
		printPhotoPathMap.put("photo4", "photo4");
		printPhotoPathMap.put("4x6 split 2up , photo5", "pic1844x1240");
	}


	/*
	 * Show dialog for selecting sub function
	 */
	void callActionSelectorDiag(final Map<String, Action> map, final Map<String, String> map2, final String title) {
		AlertDialog.Builder builderSingle = new AlertDialog.Builder(this);
		builderSingle.setTitle(title);

		final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
				this,
				android.R.layout.select_dialog_singlechoice);

		String[] strArr = new String[1];
		strArr= map != null?(String[])(map.keySet().toArray(strArr)): (String[])(map2.keySet().toArray(strArr));

		for(String str: strArr) {
			arrayAdapter.add(str);
		}

		builderSingle.setCancelable(true);

		builderSingle.setNegativeButton("cancel",
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
					}
				});

		builderSingle.setAdapter(
				arrayAdapter,
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {

						dialog.dismiss();

						String name = arrayAdapter.getItem(which);
						//printerService(name, map.get(name), null);

						if(map != null) operatePrinter(map.get(name));
						if(map2 != null) {

							switch (which)
							{
								case 4:
								{
									PaperType = 5;
								}
								break;

								default:
								{
									PaperType = 2;
								}
								break;

							}
							mSelectedPath = map2.get(name);
/*
							SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
							Date curDate = new Date(System.currentTimeMillis()) ;
							String str1 = formatter1.format(curDate);
							FileUtility.WriteFile("/storage/emulated/0/Android/data/com.hiti.test/files/Tables" +"/debug_"+str1+"_log", "** Write Debig Test \n");
*/
							operatePrinter(Action.USB_PRINT_PHOTOS);
						}

					}
				});
		builderSingle.show();
	}

	interface IFirmware {
		void updateDone(ErrorCode code);
	}

	public void updateFirmware(final IFirmware listener) {

		new AsyncTask<Object, Void, ErrorCode>() {
			@Override
			protected ErrorCode doInBackground(Object... params) {
				return operation.updateFirmware(m_fwversion, m_fwpath);
			}

			@Override
			protected void onPostExecute(ErrorCode code) {
				if(listener != null) listener.updateDone(code);
			}

		}.execute(this, m_fwpath, m_fwversion);
	}

	public void updateFirmwareP525N(final IFirmware listener) {

		new AsyncTask<Object, Void, ErrorCode>() {
			@Override
			protected ErrorCode doInBackground(Object... params) {
				return operation.updateFirmwareP525N(m_fwversion, m_fwpath, m_fwBootpath, m_fwKernelpath);
			}

			@Override
			protected void onPostExecute(ErrorCode code) {
				if(listener != null) listener.updateDone(code);
			}

		}.execute(this, m_fwpath, m_fwversion);
	}
	/*
	 * Printer operator
	 */
	void operatePrinter(final Action action) {
		Thread task = new Thread(){

			PrinterJob job = null;
			String ret = null;

			@Override
			public void run() {

				Log.i(tag, "start to do operation.. "+action.name());

				switch(action) {

					case USB_CHECK_PRINTER_STATUS:
						operation.m_strTablesRoot = m_strTablesRoot;

						//operation.m_strTablesRoot = null;
//                        operation.m_strTablesRoot = m_strTablesRoot;

                        job = operation.getPrinterStatus();
						break;

					case USB_DEVICE_MODEL_NAME:
						job = operation.getModelName();
						break;

					case USB_DEVICE_SERIAL_NUM:
						job = operation.getSerialNumber();
						break;

					case USB_DEVICE_FW_VERSION:
						job = operation.getFirmwareVersion();
						break;

					case USB_DEVICE_RIBBON_INFO:
						job = operation.getRibbonInfo();
						break;

					case USB_DEVICE_PRINT_COUNT:
						job = operation.getPrintCount();
						break;

					case USB_COMMAND_RESET_PRINTER:
						job = operation.resetPrinter();
						break;

					case USB_COMMAND_RESUME_JOB:
						job = operation.resumeJob();
						break;

					case USB_COMMAND_UPDATE_FW:
						job = operation.getModelName();
						String retVal = (String) job.retData;//retrieveData(job);
						final String strShow = retVal + " updating fw...";
						// show result to text
						runOnUiThread(new Runnable() {
							public void run() {
								m_info.append(strShow);
							}

						});

						String s1 = "P525N";
						if(retVal.equals(s1)) {

							operation.updateFirmwareP525N(m_fwversion, m_fwpath, m_fwBootpath, m_fwKernelpath);
						}
						else {

							operation.updateFirmware(m_fwversion, m_fwpath);
						}

//						updateFirmware(new IFirmware() {
//							@Override
//							public void updateDone(ErrorCode code) {
//								job.errCode = code;
//								showResponse(job);
//							}
//						});
						break;

					case USB_EJECT_PAPER_JAM:
						job = operation.ejectPaperJam();
						break;

					//case USB_CLEAN_PAPER_PATH:
					case USB_COMMAND_CLEAN_PAPER_PATH:
						job = operation.cleanPaperPath();
						break;

					case USB_PRINT_PHOTOS:
						operation.PRINTCOUNT = PRINTCOUNT;
						operation.MATTE = MATTE;
						operation.PRINTMODE = PRINTMODE;
						operation.PaperType = PaperType;
						operation.m_strTablesRoot = m_strTablesRoot;
//						Log.e("MainActivity", "mSelectedPath: "+mSelectedPath);
						job = operation.print(mSelectedPath);
						operation.m_strTablesRoot = "";
						break;

					case USB_SET_AUTO_POWER_OFF:
						job = operation.setAutoPowerOff((short)10);
						break;

					/*case USB_GET_STORAGE_ID:
						job = operation.getStorageID();
						break;

					case USB_GET_OBJECT_NUMBER:
//					long lobjectId = Long.parseLong("4",16);
//					job = operation.getObjectNumber(1, (byte)0x02, lobjectId);
						showDialog(new ICallback() {
							@Override
							public void operation(long object_id, byte formatType) {

								job = operation.getObjectNumber(1, formatType, object_id);
								showResponse(job);
							}
						});
						break;

					case USB_GET_OBJECT_HANDLE_ID:
						long lobjectId = Long.parseLong("-1",16);
						job = operation.getObjectHandleId(1, (byte)0x02, lobjectId);
						showResponse(job);
//					showDialog(new ICallback() {
//						@Override
//						public void operation(long object_id, byte formatType) {
//
//							job = operation.getObjectHandleId(1, formatType, object_id);
//							showResponse(job);
//						}
//					});
						break;

					case USB_GET_OBJECT_INFO:
//					lobjectId = Long.parseLong("16",16);
//					job = operation.getObjectInfo(1, lobjectId);
						showDialog(new ICallback() {
							@Override
							public void operation(long object_id, byte formatType) {

								job = operation.getObjectInfo(1, object_id);
								showResponse(job);
							}
						});
						break;
					case USB_GET_OBJECT_DATA:

						showDialog(new ICallback() {
							@Override
							public void operation(long object_id, byte formatType) {

								job = operation.getObjectData(1, object_id, formatType);
								showResponse(job);
							}
						});
//					lobjectId = Long.parseLong("17",16);
//					job = operation.getObjectData(1, lobjectId, (byte)0x02);
						break;*/

					default:
				}

				if(action != Action.USB_GET_OBJECT_NUMBER && action != Action.USB_GET_OBJECT_INFO
						&& action != Action.USB_GET_OBJECT_HANDLE_ID && action != Action.USB_GET_OBJECT_DATA
						&& action != Action.USB_COMMAND_UPDATE_FW)
					showResponse(job);
			}
		};

		task.start();
	}

	void showResponse(PrinterJob job) {

		final String ret = retrieveData(job);

		// show result to text
		runOnUiThread(new Runnable() {
			public void run() {
				m_info.append(ret);
			}

		});
	}

	//==================================================================================================//
	// Return data
	//==================================================================================================//

	/**
	 * Return data format.
	 *
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * |  Name of action            		   |  Action meaning       	   	  | Data type     | Data number |    data[0]    |    data[1]    |    data[2]    |    data[3]    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_PRINT_PHOTOS      	           | Print photo        	      |   -----       |    -----    |	   -----    |	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_COMMAND_RESET_PRINTER  		   | Reset printer 			      |   -----       |    -----    |	   -----    |	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_COMMAND_RESUME_JOB     		   | Clear error and resume job   |   -----       |    -----    |	   -----    |	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_CHECK_PRINTER_STATUS   		   | Printer status               | PrinterStatus | 1 object    | Printer status|	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_DEVICE_MODEL_NAME      		   | Printer model name           | String        | 1 string    | model name	|	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_DEVICE_SERIAL_NUM      		   | Manufacture serial number    | String        | 1 string    | serial number |	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_DEVICE_FW_VERSION      		   | Printer firmware version     | String        | 1 string    | FW version    |	   -----    |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_DEVICE_RIBBON_INFO     		   | Current ribbon information   | IntArray      | 2 integer   | Ribbon type   | Remain count  |	   -----    |	   -----    |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 * | USB_DEVICE_PRINT_COUNT     		   | Number of printed sheets     | IntArray      | 4 integer   | Total         | 4x6           | 5x7           | 6x8           |
	 * +------------------------------------+------------------------------+---------------+-------------+---------------+---------------+---------------+---------------+
	 */


	/**
	 * Ribbon type.
	 *
	 * +-----------------------------+------------+
	 * |  Ribbon type                |  Value     |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_YMCKO      | 0          |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_K          | 1 		 |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_KO         | 3          |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_YMCKOK     | 4          |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_HALF_YMCKO | 5          |
	 * +-----------------------------+------------+
	 * | HITI_RIBBON_TYPE_YMCKFO     | 12         |
	 * +-----------------------------+------------+
	 *
	 */

	/*
	 * According to parameter to retrieve and parsing return data
	 *
	 * @param job
	 * @return
	 */
	private String retrieveData(PrinterJob job) {

		Log.e(tag, "job.action.name: " + job.action.name());

		StringBuilder bu = new StringBuilder("\n\n<<<");

		/** get action name, job id */
		bu.append(job.action.name()).append(" -ID").append(job.getId())

				/** get error code */
				.append(" : err <0x").append(Integer.toHexString(job.errCode.value)).append(" ")
				.append(job.errCode.description).append(">");

		if(job.retData == null) {
			Log.e(tag, "no data");
			return bu.toString();
		}

		/** parsing return data */
		switch(job.action) {

			//
			// no return data
			//

			/* print photo */
			case USB_PRINT_PHOTOS:
				/* reset printer */
			case USB_COMMAND_RESET_PRINTER:
				/* clear error and continue job */
			case USB_COMMAND_RESUME_JOB:
			case USB_EJECT_PAPER_JAM:
				break;

			//
			// Check printer status
			//	printer status return data  ------>         1 PrinterStatus type object. [printer status]
			case USB_CHECK_PRINTER_STATUS:
				PrinterStatus status = ((PrinterStatus)job.retData);
				bu.append("\nStatus: 0x").append(Integer.toHexString(status.statusValue)).append(" ")
						.append(status.statusDescription);

				try{
					String description = PrinterStatus.getDescription(status.statusValue);
				}catch (Exception e){
					e.printStackTrace();
				}
				break;


			//
			// return string format.
			//
			case USB_DEVICE_MODEL_NAME:
			case USB_DEVICE_SERIAL_NUM:
			case USB_DEVICE_FW_VERSION:
			case USB_GET_STORAGE_ID:
			case USB_GET_OBJECT_NUMBER:

				/* get return data , string */
				if(job.retData instanceof String) {
					if(job.retData != null)
						bu.append("\ndata: ").append((String)job.retData);
//					try
//					{
//						FileWriter fw = new FileWriter(m_strTmpRoot+"/retData", false);
//						BufferedWriter bw = new BufferedWriter(fw);
//						bw.write((String)job.retData);
//						bw.close();
//					}
//					catch (Exception e)
//					{
//						e.printStackTrace();
//					}
				}

				break;
			case USB_GET_OBJECT_INFO:
				if(job.errCode.equals(ErrorCode.ERR_CODE_SUCCESS))
				{
					byte[] data = ((byte[]) job.retData);
					int[] date = ByteUtility.getDate(data);
					for(int time: date) bu.append(time).append(',');

					String name = ByteUtility.getEncodingName(data);

					bu.append("\ndata: ").append(name.toString());
				}
				break;
			case USB_GET_OBJECT_DATA:
				String path = GetPhoto(job);
				if(path != null) bu.append("\npath: ").append(path);
				break;
			//
			// Get ribbon information
			//	ribbon information return data  ------>     2 integer values. [Ribbon type, Remain count]
			case USB_DEVICE_RIBBON_INFO:

				//
				// Get Printer print count
				//	Printer print count  return data  ------>     4 integer values. [Total, 4x6, 5x7, 6x8]
			case USB_GET_OBJECT_HANDLE_ID:
			case USB_DEVICE_PRINT_COUNT:

				if(job.retData instanceof IntArray) {
					for(int i = 0; i< ((IntArray)job.retData).getSize(); i++) {
						bu.append("\ndata[").append(i).append("]: ")
								.append(((IntArray)job.retData).get(i));
					}
				}

				break;

			default:
				break;

		}

		Log.e(tag, "bu.toString(): "+bu.toString());
		return bu.toString();
	}


	String GetPhoto(PrinterJob job)
	{
		if(job.retData instanceof byte[]) {

			final String path = ByteUtility.byteToFile(this, (byte[])job.retData, MobileInfo.GetDateStamp()+ MobileInfo.GetHmsSStamp()+".jpg");

			if(FileUtility.FileExist(path))
			{
				Map sides = BitmapMonitor.GetPhotoTwoSide(this, Uri.fromFile(new File(path)));
				BitmapMonitorResult bmr = new BitmapMonitorResult();
				if(sides!= null)
				{
					int oriWidth  = (int)sides.get("Width");
					int oriHeight = (int)sides.get("Height");

					DisplayMetrics dm = new DisplayMetrics();
					getWindowManager().getDefaultDisplay().getMetrics(dm);
					int width = (int)dm.density*300;
					int height = (int)(width*((float)oriHeight / oriWidth));
					bmr = BitmapMonitor.CreateCroppedBitmapNew(this, Uri.fromFile(new File(path)), width, height);
				}
				final BitmapMonitorResult photo = bmr;
				if(photo.IsSuccess())
					runOnUiThread(new Runnable() {
						@Override
						public void run() {

							m_infoView.setImageBitmap(photo.GetBitmap());
						}
					});
				else
					return null;
			}

			return path;
		}

		return null;
	}


	public interface ICallback {

		void operation(long object_id, byte formatType);
	}

	class Dialog extends AlertDialog {

		Context context;
		EditText idEditView, formatEditView;
		LinearLayout view;
		Builder builder;

		protected Dialog(Context context) {
			super(context);
			this.context = context;
			view = new LinearLayout(context);
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(300, 80, 1);
			view.setLayoutParams(params);
			idEditView = new EditText(context);
			idEditView.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
			idEditView.setHint("objectId");
			formatEditView = new EditText(context);
			formatEditView.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
			formatEditView.setHint("format");

			view.addView(idEditView);
			view.addView(formatEditView);
			builder = new Builder(context, android.R.style.Theme_DeviceDefault_Light_Dialog);
			builder.setView(view);
		}


		public void build(final ICallback callback) {

			builder.setPositiveButton("OK", new OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {

					String idText 	  = idEditView.getText().toString();
					String formatEdit = formatEditView.getText().toString();
					final long id 	= idText.isEmpty()? 0: Long.parseLong(idText, 16);
					final byte format = formatEdit.isEmpty()?2: Byte.decode(formatEdit);

					new Thread(new Runnable() {
						@Override
						public void run() {

							callback.operation(id, format);
						}
					}).start();
					dialog.dismiss();
				}
			}).show();
		}
	}

	void showDialog(final ICallback callback) {

		runOnUiThread(new Runnable() {
			@Override
			public void run() {

				new Dialog(MainActivity.this).build(callback);
			}
		});
	}

	void showSimpleAlertDialog(final String message) {

		runOnUiThread(new Runnable() {
			@Override
			public void run() {

				new AlertDialog.Builder(MainActivity.this)
						.setTitle("Service")
						.setMessage(message)
						.setPositiveButton("OK", new DialogInterface.OnClickListener() {

							@Override
							public void onClick(DialogInterface dialog, int which) {

								dialog.dismiss();
							}

						}).show();
			}
		});
	}
}
